/**
 * 版权所有(C)，上海海鼎信息工程股份有限公司，2007，所有权利保留。
 * 
 * 项目名：	rumba
 * 文件名：	SysclockMgr.java
 * 模块说明：	
 * 修改历史：
 * Nov 10, 2007 - lxm - 创建。
 */
package com.hd123.rumba.sysclock;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Transaction;

import com.hd123.rumba.domain.IDomain;
import com.hd123.rumba.domain.IDomainMgr;
import com.hd123.rumba.exception.BusinessException;
import com.hd123.rumba.mscb.constraint.ConstraintFactory;
import com.hd123.rumba.mscb.constraint.IConstraint;
import com.hd123.rumba.mscb.order.CMscbOrder;
import com.hd123.rumba.mscb.order.IMscbOrder;
import com.hd123.rumba.mscb.order.IMscbOrderMgr;
import com.hd123.rumba.mscb.order.biz.BMscbOrder;
import com.hd123.rumba.mscb.order.biz.IBMscbMgr;
import com.hd123.rumba.option.biz.IBOptionMgr;
import com.hd123.rumba.runtime.IAfterSave;
import com.hd123.rumba.runtime.IAfterSaveNew;
import com.hd123.rumba.runtime.IManager;
import com.hd123.rumba.runtime.NamingMgr;
import com.hd123.rumba.runtime.ObserverRegistry;
import com.hd123.rumba.runtime.OperateContext;
import com.hd123.rumba.runtime.backprocess.Task;
import com.hd123.rumba.runtime.backprocess.TaskMgr;
import com.hd123.rumba.runtime.biz.BManager;
import com.hd123.rumba.user.IUser;
import com.hd123.rumba.user.IUserMgr;

/**
 * 系统时钟 | 管理员 | 实现类
 * <p>
 * 
 * 需要侦听消息订阅单，当subject是系统时钟，其新增、修改或删除发生后，应自动判断对相应的后台任务的执行。
 * 
 * @author lxm
 * 
 */
public class SysclockMgr extends BManager implements ISysclockMgr, IAfterSaveNew, IAfterSave {

  public SysclockMgr() {
    ObserverRegistry.register(IMscbOrder.class, IAfterSaveNew.class, this);
    ObserverRegistry.register(IMscbOrder.class, IAfterSave.class, this);
  }

  @Override
  public IManager getPerzMgr() throws BusinessException {
    throw BusinessException.neverCallMe();
  }

  public void doAfterSaveNew(Object obj) throws BusinessException {
    if (!(obj instanceof IMscbOrder))
      return;
    this.onOrderChanged((IMscbOrder) obj);
  }

  public void doAfterSave(Object obj) throws BusinessException {
    if (!(obj instanceof IMscbOrder))
      return;
    this.onOrderChanged((IMscbOrder) obj);
  }

  public void onBoot() throws BusinessException {
    this.arrangeTask();
  }

  public void updateBufferByNotifyTimeT(Date notifyTime) throws BusinessException {
    this.checkParameterNotNull(notifyTime, "notifyTime");

    try {
      Transaction tx = getSession().beginTransaction();
      try {
        TaskScheduleBuffer buffer = this.getOptMgr().loadCollection(this.getRootDomain(),
            TaskScheduleBuffer.class, null);
        assert buffer != null;

        OperateContext operateCtx = new OperateContext(this.getSysUser());

        // 扫描缓冲中每个单据的当前下次触发时间<=本次触发时间的消息订阅单，重新计算下次触发时间。
        for (String orderUuid : buffer.getOrderUuids()) {
          Date nextTime = buffer.getNextTime(orderUuid);
          if (notifyTime.before(nextTime))
            continue;

          IMscbOrder order = this.getOrderMgr().getValidOrder(orderUuid);
          if (order == null) {
            buffer.removeOrderInfo(orderUuid);
            continue;
          }
          IConstraint cst = ConstraintFactory.buildConstraint(order.getConstraintClassName());
          if (cst == null || !(cst instanceof IForTaskSchedule)) {
            buffer.removeOrderInfo(orderUuid);
            continue;
          }
          cst.setContext(order.getConstraintContext());
          Date newNextTime = ((IForTaskSchedule) cst).getNextTime(notifyTime);
          if (newNextTime == null) {
            buffer.removeOrderInfo(orderUuid);
            this.cancelMscbOrder(orderUuid, operateCtx);
          } else
            buffer.setNextTime(orderUuid, newNextTime);
        }

        this.getOptMgr().saveCollection(buffer, operateCtx);
        tx.commit();
      } catch (BusinessException e) {
        tx.rollback();
        throw e;
      } catch (HibernateException e) {
        tx.rollback();
        throw e;
      } finally {
        getSession().flush();
      }
    } catch (HibernateException e) {
      throw new BusinessException(e);
    }
  }

  public void arrangeTask() throws BusinessException {
    try {
      TaskScheduleBuffer buffer = this.getOptMgr().loadCollection(this.getRootDomain(),
          TaskScheduleBuffer.class, null);
      Date nextTime = buffer == null ? null : buffer.getEarliestNextTime();

      if (nextTime == null) {
        this.doUnregisterTask();
      } else {
        this.doRegisterTask(nextTime);
      }
    } catch (HibernateException e) {
      throw new BusinessException(e);
    }
  }

  private SysclockTaskSchedule schedule = null;
  private SysclockTaskNature nature = null;
  private IDomain rootDomain = null;
  private IUser sysUser = null;
  private IBOptionMgr optMgr = null;
  private IMscbOrderMgr orderMgr = null;
  private IBMscbMgr mscbMgr = null;

  private SysclockTaskSchedule getSchedule() {
    if (schedule == null)
      schedule = new SysclockTaskSchedule();
    return schedule;
  }

  private SysclockTaskNature getNature() throws BusinessException {
    if (nature == null) {
      nature = SysclockTaskNature.create();
      nature.setDomain(this.getRootDomain());
      nature.setOperator(this.getSysUser());
    }
    return nature;
  }

  private IDomain getRootDomain() throws BusinessException {
    try {
      if (rootDomain == null) {
        IDomainMgr dmMgr = (IDomainMgr) NamingMgr.getInstance().lookupMgr(IDomainMgr.class);
        rootDomain = dmMgr.getRootDomain();
      }
      return rootDomain;
    } catch (HibernateException e) {
      throw new BusinessException(e);
    }
  }

  private IUser getSysUser() throws BusinessException {
    if (sysUser == null) {
      IUserMgr userMgr = (IUserMgr) NamingMgr.getInstance().lookupMgr(IUserMgr.class);
      sysUser = userMgr.getSysUser();
    }
    return sysUser;
  }

  private IBOptionMgr getOptMgr() throws BusinessException {
    if (this.optMgr == null)
      this.optMgr = (IBOptionMgr) NamingMgr.getInstance().lookupMgr(IBOptionMgr.class);
    return this.optMgr;
  }

  private IMscbOrderMgr getOrderMgr() throws BusinessException {
    if (this.orderMgr == null)
      this.orderMgr = (IMscbOrderMgr) NamingMgr.getInstance().lookupMgr(IMscbOrderMgr.class);
    return this.orderMgr;
  }

  private IBMscbMgr getMscbMgr() throws BusinessException {
    if (this.mscbMgr == null)
      this.mscbMgr = (IBMscbMgr) NamingMgr.getInstance().lookupMgr(IBMscbMgr.class);
    return this.mscbMgr;
  }

  private void onOrderChanged(IMscbOrder order) throws BusinessException {
    if (!SysclockSubject.class.getName().equals(order.getSubjectClass()))
      return;
    if (!SysclockSubject.VALUE_EVENT_ONTICK.equals(order.getEvent()))
      return;

    try {
      TaskScheduleBuffer buffer = this.getOptMgr().loadCollection(this.getRootDomain(),
          TaskScheduleBuffer.class, null);
      if (buffer == null)
        buffer = this.getOptMgr().createCollection(this.getRootDomain(), TaskScheduleBuffer.class,
            null);

      // 修改buffer
      if (CMscbOrder.VALUE_STATE_DELETED.equals(order.getState())) {
        buffer.removeOrderInfo(order.getUuid());
      } else {
        IConstraint cst = ConstraintFactory.buildConstraint(order.getConstraintClassName());
        if (cst == null || !(cst instanceof IForTaskSchedule))
          return;
        cst.setContext(order.getConstraintContext());
        Date nextTime = buffer.getNextTime(order.getUuid());
        if (nextTime == null)
          nextTime = ((IForTaskSchedule) cst).getNextTime(null);
        else {
          Map<String, Object> dict = new HashMap<String, Object>();
          dict.put(CSysclock.DICT_NOTIFYTIME, nextTime);
          cst.setDict(dict);
          if (!cst.isValid())
            nextTime = ((IForTaskSchedule) cst).getNextTime(null);
          else {
            Date time = ((IForTaskSchedule) cst).getNextTime(null);
            if (time != null && time.before(nextTime))
              nextTime = time;
          }
        }
        buffer.setNextTime(order.getUuid(), nextTime);
      }
      this.getOptMgr().saveCollection(buffer, new OperateContext(this.getSysUser()));

      this.arrangeTask();

    } catch (HibernateException e) {
      throw new BusinessException(e);
    }
  }

  private void doUnregisterTask() throws BusinessException {
    List<Task> tasks = TaskMgr.getInstance().getTasksByNature(this.getNature());
    for (Task task : tasks)
      TaskMgr.getInstance().unregisterTask(task);
  }

  private void doRegisterTask(Date notifyTime) throws BusinessException {
    List<Task> tasks = TaskMgr.getInstance().getTasksByNature(this.getNature());
    if (tasks.size() > 0) {
      if (((SysclockTaskNature) tasks.get(0).getNature()).getNotifyTime().equals(notifyTime))
        return;
      else
        this.doUnregisterTask();
    }
    this.getNature().setNotifyTime(notifyTime);
    Task task = TaskMgr.getInstance().registerTask(this.getNature(), this.getSchedule());
    // 因为schedule无法向task传递正确的触发时间，因此需要手工通知task触发时间。
    task.start(notifyTime);
  }

  private void cancelMscbOrder(String orderUuid, OperateContext operate) throws BusinessException,
      HibernateException {
    BMscbOrder order = this.getMscbMgr().getOrder(orderUuid, BMscbOrder.class);
    this.getMscbMgr().cancelOrder(order, order.getSubscriber(), operate);
  }
}
